From 63898815b4203c606350dc41c4edeaf17e3bb8c5 Mon Sep 17 00:00:00 2001 From: Tim Deegan Date: Thu, 30 Jun 2011 10:26:54 +0100 Subject: [PATCH] x86/p2m: Add p2m_change_type_range() operation that defers the nested-p2m flush until the entire batch has been updated. Use it in the HAP log-dirty operations for tracking VRAM changes. This should avoid a lot of unpleasant IPI storms as the log-dirty code on one CPU repeatedly shoots down the nested p2m of another CPU. Signed-off-by: Tim Deegan --- xen/arch/x86/mm/hap/hap.c | 18 ++++++++---------- xen/arch/x86/mm/p2m.c | 31 +++++++++++++++++++++++++++++++ xen/include/asm-x86/p2m.h | 11 +++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c index 92cf2690ab..f90511ef04 100644 --- a/xen/arch/x86/mm/hap/hap.c +++ b/xen/arch/x86/mm/hap/hap.c @@ -58,7 +58,6 @@ static int hap_enable_vram_tracking(struct domain *d) { - int i; struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; if ( !dirty_vram ) @@ -70,8 +69,8 @@ static int hap_enable_vram_tracking(struct domain *d) paging_unlock(d); /* set l1e entries of P2M table to be read-only. */ - for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++) - p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty); + p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, + p2m_ram_rw, p2m_ram_logdirty); flush_tlb_mask(d->domain_dirty_cpumask); return 0; @@ -79,7 +78,6 @@ static int hap_enable_vram_tracking(struct domain *d) static int hap_disable_vram_tracking(struct domain *d) { - int i; struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; if ( !dirty_vram ) @@ -90,8 +88,8 @@ static int hap_disable_vram_tracking(struct domain *d) paging_unlock(d); /* set l1e entries of P2M table with normal mode */ - for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++) - p2m_change_type(d, i, p2m_ram_logdirty, p2m_ram_rw); + p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, + p2m_ram_logdirty, p2m_ram_rw); flush_tlb_mask(d->domain_dirty_cpumask); return 0; @@ -99,15 +97,14 @@ static int hap_disable_vram_tracking(struct domain *d) static void hap_clean_vram_tracking(struct domain *d) { - int i; struct sh_dirty_vram *dirty_vram = d->arch.hvm_domain.dirty_vram; if ( !dirty_vram ) return; /* set l1e entries of P2M table to be read-only. */ - for (i = dirty_vram->begin_pfn; i < dirty_vram->end_pfn; i++) - p2m_change_type(d, i, p2m_ram_rw, p2m_ram_logdirty); + p2m_change_type_range(d, dirty_vram->begin_pfn, dirty_vram->end_pfn, + p2m_ram_rw, p2m_ram_logdirty); flush_tlb_mask(d->domain_dirty_cpumask); } @@ -863,7 +860,8 @@ hap_write_p2m_entry(struct vcpu *v, unsigned long gfn, l1_pgentry_t *p, paging_lock(d); old_flags = l1e_get_flags(*p); - if ( nestedhvm_enabled(d) && (old_flags & _PAGE_PRESENT) ) { + if ( nestedhvm_enabled(d) && (old_flags & _PAGE_PRESENT) + && !p2m_get_hostp2m(d)->defer_nested_flush ) { /* We are replacing a valid entry so we need to flush nested p2ms, * unless the only change is an increase in access rights. */ mfn_t omfn = _mfn(l1e_get_pfn(*p)); diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c index 9592b48030..a8f18ac484 100644 --- a/xen/arch/x86/mm/p2m.c +++ b/xen/arch/x86/mm/p2m.c @@ -537,6 +537,37 @@ p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn, return pt; } +/* Modify the p2m type of a range of gfns from ot to nt. + * Resets the access permissions. */ +void p2m_change_type_range(struct domain *d, + unsigned long start, unsigned long end, + p2m_type_t ot, p2m_type_t nt) +{ + p2m_type_t pt; + unsigned long gfn; + mfn_t mfn; + struct p2m_domain *p2m = p2m_get_hostp2m(d); + + BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); + + p2m_lock(p2m); + p2m->defer_nested_flush = 1; + + for ( gfn = start; gfn < end; gfn++ ) + { + mfn = gfn_to_mfn_query(d, gfn, &pt); + if ( pt == ot ) + set_p2m_entry(p2m, gfn, mfn, 0, nt, p2m->default_access); + } + + p2m->defer_nested_flush = 0; + if ( nestedhvm_enabled(d) ) + p2m_flush_nestedp2m(d); + p2m_unlock(p2m); +} + + + int set_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn) { diff --git a/xen/include/asm-x86/p2m.h b/xen/include/asm-x86/p2m.h index ff3f1a0054..c39ebd2c55 100644 --- a/xen/include/asm-x86/p2m.h +++ b/xen/include/asm-x86/p2m.h @@ -209,6 +209,12 @@ struct p2m_domain { #define CR3_EADDR (~0ULL) uint64_t cr3; + /* Host p2m: when this flag is set, don't flush all the nested-p2m + * tables on every host-p2m change. The setter of this flag + * is responsible for performing the full flush before releasing the + * host p2m's lock. */ + int defer_nested_flush; + /* Pages used to construct the p2m */ struct page_list_head pages; @@ -408,6 +414,11 @@ int guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, void p2m_change_entry_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt); +/* Change types across a range of p2m entries (start ... end-1) */ +void p2m_change_type_range(struct domain *d, + unsigned long start, unsigned long end, + p2m_type_t ot, p2m_type_t nt); + /* Compare-exchange the type of a single p2m entry */ p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn, p2m_type_t ot, p2m_type_t nt); -- 2.30.2